/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package generator

import java.io.File
import java.io.PrintWriter

import com.google.protobuf.Descriptors.Descriptor
import com.google.protobuf.Descriptors.EnumDescriptor

import aggr.{SerializedTypes => ST}
import parser.Nodes._
import parser.DataTypes._
import parser.Results._

/**
 * The module to generate Java source file where "map" function is defined.
 */
object Mapper {

  import Generator.staticVarName

  /**
   * Generates Java source file.
   *
   * TODO: Too many arguments.
   *
   * @param dir The directory where source file is created.
   */
  def generate(dir: File, 
               convertersClassName: String, 
               svarClassName: String, 
               msgs: List[Descriptor], 
               enums: List[EnumDescriptor], 
               typeM: TypeMapper,
               exprC: ExprConverter,
               enclosingVars: Set[Int],
               tables: List[Table],
               ast: List[Stmt]): File = {

    Generator.genSrc(dir, "Mapper", None, List("Helpers.Mapper")) { pw =>
      // Members
      pw.println(
        "\tpublic Descriptor[] messages;\n" +
        "\tpublic EnumDescriptor[] enums;\n" +
        "\tpublic %s converters;\n".format(convertersClassName) +
        "\tpublic %s statics;".format(svarClassName))

      // Constructor
      pw.println(
        "public Mapper(Descriptor[] m, EnumDescriptor[] e, %s s) {\n".format(svarClassName) +
        "this.messages   = m;\n" +
        "this.enums      = e ;\n" +
        "this.converters = new %s(m);\n".format(convertersClassName) +
        "this.statics    = s;\n")

      printTableInit(pw, msgs, enums, tables)

      pw.println("}")

      pw.println("@Override public void map(Emitter emitter, Helpers.ByteStringWrapper global_0_input) throws java.lang.Throwable {")
      StmtPrinter.print(pw, typeM, exprC, enclosingVars, ast)
      pw.println("}")
    }._1
  }

  /**
   * Prints code for initialize tables.
   */
  private def printTableInit(pw: PrintWriter,
                     msgs: List[Descriptor],
                     enums: List[EnumDescriptor],
                     tables: List[Table]): Unit = {

    val msgMap  = Map() ++ msgs.zipWithIndex.map { case (x, n) => (x.getName(), n) }
    val enumMap = Map() ++ enums.zipWithIndex.map { case (x, n) => (x.getName(), n) }

    def serialType(dataType: ST.SerializedType): String = dataType match {
      case ST.BoolType       => "aggr.SerializedTypes$BoolType$.MODULE$"
      case ST.FloatType      => "aggr.SerializedTypes$FloatType$.MODULE$"
      case ST.DoubleType     => "aggr.SerializedTypes$DoubleType$.MODULE$"
      case ST.Fixed32Type    => "aggr.SerializedTypes$Fixed32Type$.MODULE$"
      case ST.Fixed64Type    => "aggr.SerializedTypes$Fixed64Type$.MODULE$"
      case ST.SFixed32Type   => "aggr.SerializedTypes$SFixed32Type$.MODULE$"
      case ST.SFixed64Type   => "aggr.SerializedTypes$SFixed64Type$.MODULE$"
      case ST.Int32Type      => "aggr.SerializedTypes$Int32Type$.MODULE$"
      case ST.Int64Type      => "aggr.SerializedTypes$Int64Type$.MODULE$"
      case ST.SInt32Type     => "aggr.SerializedTypes$SInt32Type$.MODULE$"
      case ST.SInt64Type     => "aggr.SerializedTypes$SInt64Type$.MODULE$"
      case ST.UInt32Type     => "aggr.SerializedTypes$UInt32Type$.MODULE$"
      case ST.UInt64Type     => "aggr.SerializedTypes$UInt64Type$.MODULE$"
      case ST.StringType     => "aggr.SerializedTypes$StringType$.MODULE$"
      case ST.BytesType      => "aggr.SerializedTypes$BytesType$.MODULE$"
      case ST.EmptyType      => "aggr.SerializedTypes$.MODULE$.EmptyType()"
      case x: ST.EnumType    => "new aggr.SerializedTypes.EnumType(enums[%d])".format(enumMap(x.descriptor.getName))
      case x: ST.MessageType => "new aggr.SerializedTypes.MessageType(messages[%d])".format(msgMap(x.descriptor.getName))
    }

    for (table <- tables) {
      pw.println(
        "statics.%s = new Helpers.Table(%d, %s, %s);".format(
          staticVarName(table),
          table.id, 
          serialType(table.sInfo.key), 
          serialType(table.sInfo.value)))
    }
  }

}
